package com.aispeech.jarpc.message;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.Charset;

@Slf4j
public class ArpcMessage {
    public final static int MAX_MESSAGE_SIZE = 1 << 27;
    public final static int MESSAGE_HEADER_LENGTH = 9;

    private EMsgType msgType;
    private int sn;
    private int length;
    private byte[] body;

    private ArpcMessage(byte[] header) {
        if (header.length < MESSAGE_HEADER_LENGTH)
            throw new RuntimeException("message length less than " + MESSAGE_HEADER_LENGTH);

        this.msgType = EMsgType.from(header[0]);
        this.sn = (header[1] & 0xff) | ((header[2] << 8) & 0xff00) | ((header[3] << 24) >>> 8) | (header[4] << 24);
        this.length = (header[5] & 0xff) | ((header[6] << 8) & 0xff00) | ((header[7] << 24) >>> 8) | (header[8] << 24);
        this.body = new byte[0];
    }

    public ArpcMessage(EMsgType msgType, int sn, byte[] body) {
        this.msgType = msgType;
        this.sn = sn;
        this.length = body.length;
        this.body = body;
    }

    public ArpcMessage(EMsgType msgType, int sn, byte[] body, int len) {
        this.msgType = msgType;
        this.sn = sn;
        this.length = len;
        this.body = body;
    }

    public static ArpcMessage from(byte[] header) {
        return new ArpcMessage(header);
    }

    private static byte[] intToByte(int val) {
        byte[] b = new byte[4];
        b[0] = (byte) (val & 0xff);
        b[1] = (byte) ((val >> 8) & 0xff);
        b[2] = (byte) ((val >> 16) & 0xff);
        b[3] = (byte) ((val >> 24) & 0xff);

        return b;
    }

    public ByteBuffer asByteBuffer() throws IOException {
        ByteBuffer byteBuffer = ByteBuffer.allocate(MESSAGE_HEADER_LENGTH + length);
        byteBuffer.order(ByteOrder.LITTLE_ENDIAN);

        byteBuffer.put(msgType.getValue());
        byteBuffer.putInt(sn);
        byteBuffer.putInt(length);

        for (int i = 0; i < length; i++) {
            byteBuffer.put(body[i]);
        }

        byteBuffer.flip();

        return byteBuffer;
    }

    public EMsgType getMsgType() {
        return msgType;
    }

    public int getSn() {
        return sn;
    }

    public int getLength() {
        return length;
    }

    public byte[] getBody() {
        return body;
    }

    public void setBody(byte[] body) {
        this.body = body;
    }

    @Override
    public String toString() {
        return String.format("Msg(type:%s,sn:%d,length:%d):%s", msgType, sn, length, msgType == EMsgType.BINARY ? body : new String(body, Charset.defaultCharset()));
    }
}
